package com.irain.pp.tool.api.syn;


import com.irain.pp.tool.ExceptionUtils;
import com.irain.pp.tool.api.syn.exception.AlreadyLockedException;

/**
 * @author WangYanPeng
 * @date 2017/11/2 14:44
 */
public class LockHelper {

    private static SyncLockSupport template;

    private static long defaultExpire = 10000L;

    private LockHelper(){}

    public static void register(SyncLockSupport template) {
        if (LockHelper.template != null){
            throw new IllegalArgumentException("LockHelper has two service: "
                    + template.getClass().getName() + " AND " + LockHelper.template.getClass().getName());
        }
        LockHelper.template = template;
    }

    public static <T> T syncLock(Object lock, LockCallBack<T> callBack) {
        return syncLock(lock.getClass(), new String[]{}, callBack, defaultExpire, 0);
    }

    public static <T> T syncLock(Object lock, String keyWord, LockCallBack<T> callBack) {
        return syncLock(lock.getClass(), new String[]{keyWord}, callBack, defaultExpire, 0);
    }

    public static <T> T syncLock(Object lock, String[] keyWords, LockCallBack<T> callBack) {
        return syncLock(lock.getClass(), keyWords, callBack, defaultExpire, 0);
    }

    public static <T> T syncLock(Class lock, LockCallBack<T> callBack) {
        return syncLock(lock, new String[]{}, callBack, defaultExpire, 0);
    }

    public static <T> T syncLock(Class lock, String keyWord, LockCallBack<T> callBack) {
        return syncLock(lock, new String[]{keyWord}, callBack, defaultExpire, 0);
    }

    public static <T> T syncLock(Class lock, String[] keyWords, LockCallBack<T> callBack) {
        return syncLock(lock, keyWords, callBack, defaultExpire, 0);
    }

    public static <T> T syncLock(Object lock, LockCallBack<T> callBack, long expire, long timeout) {
        return syncLock(lock.getClass(), new String[]{}, callBack, expire, timeout);
    }

    public static <T> T syncLock(Object lock, String keyWord, LockCallBack<T> callBack, long expire, long timeout) {
        return syncLock(lock.getClass(), new String[]{keyWord}, callBack, expire, timeout);
    }

    public static <T> T syncLock(Object lock, String[] keyWords, LockCallBack<T> callBack, long expire, long timeout) {
        return syncLock(lock.getClass(), keyWords, callBack, expire, timeout);
    }

    public static <T> T syncLock(Class lock, LockCallBack<T> callBack, long expire, long timeout) {
        return syncLock(lock, new String[]{}, callBack, expire, timeout);
    }

    public static <T> T syncLock(Class lock, String keyWord, LockCallBack<T> callBack, long expire, long timeout) {
        return syncLock(lock, new String[]{keyWord}, callBack, expire, timeout);
    }

    public static <T> T syncLock(Class lock, String[] keyWords, LockCallBack<T> callBack, long expire, long timeout) {
        final long preExpire = preExpire(expire);
        final long preTimeout = preTimeout(timeout);
        boolean isSuccess = template.lock(lock, keyWords, preExpire, preTimeout);
        if (!isSuccess) {
            throw new AlreadyLockedException();
        }
        try {
            return callBack.execute();
        } catch (Throwable e) {
            throw ExceptionUtils.transfer(e);
        } finally {
            template.unlock(lock, keyWords);
        }
    }

    private static long preExpire(final long expire) {
        return expire <= 0L ? defaultExpire : expire;
    }

    private static long preTimeout(final long timeout) {
        return timeout < 0L ? 0L : timeout;
    }
}
